﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Isuka.Iteration;
using Isuka.Linq;
using Isuka.Spiritual.Auxiliaries;
using System.Text.RegularExpressions;

namespace Isuka.Spiritual
{

    public partial class Rules<T, A>
        where T : IEquatable<T>
        where A : class
    {

        /// <summary>
        /// ルールのシグネチャー
        /// 引数 it は書き換わる可能性がある
        /// </summary>
        /// <param name="it"></param>
        /// <param name="context"></param>
        /// <param name="skipper"></param>
        /// <returns></returns>
        public delegate RuleResult<T> RuleFunc(IBidirectionalIterator<T> it, RuleContext<T, A> context, SkipFunc skipper);

        /// <summary>
        /// スキップパーサー
        /// 引数 it は書き換わる可能性がある
        /// </summary>
        /// <param name="it"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public delegate IBidirectionalIterator<T> SkipFunc(IBidirectionalIterator<T> it, RuleContext<T, A> context);

        /// <summary>
        /// あらゆる値に一致する
        /// </summary>
        public static readonly RuleHolder<T, A> Any = new RuleHolder<T, A>((i, c, s) =>
        {
            i = s(i, c);
            if (!i.Equals(c.Last))
            {
                i.MoveNext();
                return _True(i);
            }
            else
            {
                return _False(i);
            }
        });


        public static RuleHolder<T, A> Case(params T[] values)
        {
            Contract.Requires(values != null);
            Contract.Ensures(Contract.Result<RuleHolder<T, A>>() != null);

            return new RuleHolder<T, A>((i, c, s) =>
            {
                i = s(i, c);
                if (!i.Equals(c.Last) && values.Any(v => i.Current.Equals(v)))
                {
                    var x = i.Current;
                    if (Array.Exists(values, v => x.Equals(v)))
                    {
                        i.MoveNext();
                        return _True(i);
                    }
                }
                return _False(i);
            });
        }


        public static RuleHolder<T, A> Case(IEnumerable<IRule<T, A>> rules)
        {
            Contract.Requires(rules != null);
            Contract.Ensures(Contract.Result<RuleHolder<T, A>>() != null);

            return new RuleHolder<T, A>((i, c, s) =>
            {
                foreach (var rule in rules)
                {
                    var r = rule.Rule(i.Copy(), c, s);
                    if (r.Match)
                    {
                        return r;
                    }
                }
                return _False(i);
            });
        }

        public static RuleHolder<T, A> Enum(IRule<T, A> rule, IRule<T, A> delim)
        {
            Contract.Requires<ArgumentNullException>(rule != null, "rule");
            Contract.Requires<ArgumentNullException>(delim != null, "delim");
            Contract.Ensures(Contract.Result<RuleHolder<T, A>>() != null);

            return new RuleHolder<T, A>(rule.Rule.Pipe(delim.Rule.Pipe(rule.Rule).ZeroOrMore()));
        }


        /// <summary>
        /// 空のルール
        /// </summary>
        public static readonly RuleHolder<T, A> Epsilon = new RuleHolder<T, A>((i, c, s) => _True(i));

        /// <summary>
        /// 値が一致するかどうか
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static RuleHolder<T, A> Eq(T value)
        {
            Contract.Ensures(Contract.Result<RuleHolder<T, A>>() != null);

            return new RuleHolder<T, A>(_Eq(value));
        }

        /// <summary>
        /// 入力の末尾に一致
        /// </summary>
        public static readonly RuleHolder<T, A> EndOfSource = new RuleHolder<T, A>((i, c, s) =>
        {
            i = s(i, c);
            return _Result(i, !i.InRange || i.Equals(c.Last));
        });

        /// <summary>
        /// シーケンスに一致するかどうか
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static RuleHolder<T, A> Eq(IEnumerable<T> values)
        {
            Contract.Requires<ArgumentNullException>(values != null, "values");

            return new RuleHolder<T, A>(_Eq(values));
        }

        /// <summary>
        /// 必ず失敗する
        /// </summary>
        public static readonly RuleHolder<T, A> Fail = new RuleHolder<T, A>((i, c, s) => _False(i));

        /// <summary>
        /// 値が条件に一致するかどうか
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static RuleHolder<T, A> If(Func<T, bool> predicate)
        {
            Contract.Requires<ArgumentNullException>(predicate != null, "predicate");

            return new RuleHolder<T, A>((i, c, s) =>
            {
                i = s(i, c);
                if (!i.Equals(c.Last) && predicate(i.Current))
                {
                    i.MoveNext();
                    return _True(i);
                }
                else
                {
                    return _False(i);
                }
            });
        }

        public static RuleHolder<T, A> NoSkipScope(IRule<T, A> rule)
        {
            Contract.Requires<ArgumentNullException>(rule != null, "rule");
            Contract.Ensures(Contract.Result<RuleHolder<T, A>>() != null);

            return new RuleHolder<T, A>((i, c, s) =>
            {
                return rule.Rule(i, c, NoSkip);
            });
        }

        public static RuleHolder<T, A> SkipperScope(IRule<T, A> rule, SkipFunc push)
        {
            Contract.Requires<ArgumentNullException>(rule != null, "rule");
            Contract.Requires<ArgumentNullException>(push != null, "push");
            Contract.Ensures(Contract.Result<RuleHolder<T, A>>() != null);

            return new RuleHolder<T, A>((i, c, s) =>
            {
                return rule.Rule(i, c, push);
            });
        }

        public static RuleHolder<T, A> While(Func<T, bool> predicate, int min = 1, int max = -1)
        {
            Contract.Requires<ArgumentOutOfRangeException>(0 <= min);
            Contract.Requires<ArgumentOutOfRangeException>(max == -1 || min <= max);
            Contract.Ensures(Contract.Result<RuleHolder<T, A>>() != null);

            if (max == -1)
            {
                return new RuleHolder<T, A>((i, c, s) =>
                {
                    i = s(i, c);
                    var count = 0;
                    while (!i.Equals(c.Last) && predicate(i.Current))
                    {
                        i.MoveNext();
                        count++;
                    }
                    return _Result(i, min <= count);
                });
            }
            else
            {
                return new RuleHolder<T, A>((i, c, s) =>
                {
                    i = s(i, c);
                    var count = 0;
                    while (!i.Equals(c.Last) && predicate(i.Current) && count != max)
                    {
                        i.MoveNext();
                        count++;
                    }
                    return _Result(i, min <= count);
                });
            }
        }

        public static RuleHolder<T, A> ZeroWidth(Func<T, bool> predicate)
        {
            Contract.Requires<ArgumentNullException>(predicate != null, "predicate");
            Contract.Ensures(Contract.Result<RuleHolder<T, A>>() != null);

            return new RuleHolder<T, A>((i, c, s) =>
            {
                return _Result(i, !i.Equals(c.Last) && predicate(i.Current));
            });
        }

        public static RuleHolder<T, A> ZeroWidth(IRule<T,A> rule)
        {
            Contract.Requires<ArgumentNullException>(rule != null, "rule");
            Contract.Ensures(Contract.Result<RuleHolder<T, A>>() != null);

            return new RuleHolder<T, A>((i, c, s) =>
            {
                var saved = i.Copy();
                return _Result(saved, rule.Rule(i, c, s).Match);
            });
        }

        /// <summary>
        /// RuleContext を作成
        /// </summary>
        /// <param name="iterable"></param>
        /// <param name="arg"></param>
        /// <returns></returns>
        public static RuleContext<T, A> CreateContext(IIterable<IBidirectionalIterator<T>> iterable, A arg)
        {
            Contract.Requires<ArgumentNullException>(iterable != null, "iterable");
            Contract.Ensures(Contract.Result<RuleContext<T, A>>() != null);

            return new RuleContext<T, A>(iterable.Begin(), iterable.End(), arg);
        }

        public static RuleReference<T, A> NewRule()
        {
            Contract.Ensures(Contract.Result<RuleReference<T, A>>() != null);

            return new RuleReference<T, A>();
        }

        /// <summary>
        /// 1要素を文字列化した状態で、正規表現にマッチするかどうか
        /// </summary>
        /// <param name="regex"></param>
        /// <returns></returns>
        public static RuleHolder<T, A> RegexOne(Regex regex)
        {
            return If(x => regex.IsMatch(x.ToString()));
        }
    }
}
